package com.wsm.util.httpclient.proxy;

import com.wsm.util.httpclient.annotation.Body;
import com.wsm.util.httpclient.annotation.Get;
import com.wsm.util.httpclient.annotation.Post;
import com.wsm.util.httpclient.config.LogConfig;
import com.wsm.util.httpclient.util.HttpClientUtil;
import com.wsm.util.httpclient.util.JsonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 王士敏
 */
public class HttpRequestProxy implements InvocationHandler {

    private final Logger log;

    private final Class<?> clazz;

    private final String baseUrl;

    private final boolean logTime;

    private final boolean logResponse;

    private final boolean logUrl;

    private final Integer retryCount;

    private static final String SPLIT = "/";

    public HttpRequestProxy(Class<?> interfaceClass, LogConfig logConfig, Integer retryCount, String baseUrl) {
        this.clazz = interfaceClass;
        this.baseUrl = baseUrl;
        this.logTime = logConfig.getTime();
        this.logResponse = logConfig.getResponse();
        this.logUrl = logConfig.getUrl();
        this.retryCount = retryCount;
        this.log = LoggerFactory.getLogger(clazz);
    }

    public <T> T proxy() {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        Parameter[] params = method.getParameters();
        Map<String, Object> httpMap = new HashMap<>(params.length);
        int length = args.length;
        for (int i = 0; i < length; i++) {
            httpMap.put(params[i].getName(), args[i]);
        }
        Post post = method.getAnnotation(Post.class);
        if (method.getAnnotation(Post.class) != null) {
            return post(method.getName(), splitUrl(post.value()), httpMap, method.getReturnType(), checkBody(method.getParameterAnnotations()));
        }

        Get get = method.getAnnotation(Get.class);
        if (get != null) {
            return get(method.getName(), splitUrl(get.value()), httpMap, method.getReturnType());
        }
        return null;
    }

    private boolean checkBody(Annotation[][] parameterAnnotations) {
        boolean isJson = false;
        for (Annotation[] paramAnn : parameterAnnotations) {
            for (Annotation annotation : paramAnn) {
                //这里判断当前注解是否为Body.class
                if (annotation.annotationType().equals(Body.class)) {
                    isJson = true;
                    break;
                }
            }
        }
        return isJson;
    }

    private String splitUrl(String path) {
        if (!path.startsWith(SPLIT)) {
            path = SPLIT + path;
        }
        return this.baseUrl + path;
    }

    private Object post(String methodName, String url, Map<String, Object> param, Class<?> returnType, boolean isJson) {
        long startTime = System.currentTimeMillis();
        int count = 0;
        Object execute = null;
        while (count < retryCount) {
            count++;
            try {
                execute =
                        JsonUtil.string2Obj(
                                isJson ?
                                        HttpClientUtil.doPostByJson(url, null, param)
                                        :
                                        HttpClientUtil.doPostByForm(url, null, param),
                                returnType);
                break;
            } catch (Exception e) {
                ifRetry(methodName, count, e);
            }
        }
        logs(methodName, url, startTime, execute);
        return execute;
    }

    private Object get(String methodName, String url, Map<String, Object> param, Class<?> returnType) {
        long startTime = System.currentTimeMillis();
        int count = 0;
        Object execute = null;
        while (count < retryCount) {
            count++;
            try {
                execute = JsonUtil.string2Obj(HttpClientUtil.doGet(url, null, param), returnType);
                break;
            } catch (Exception e) {
                ifRetry(methodName, count, e);
            }
        }
        logs(methodName, url, startTime, execute);
        return execute;
    }

    /**
     * 异常信息处理
     */
    private void ifRetry(String methodName, int count, Exception e) {
        log.warn("HttpClient-util:[" + clazz.getName() + "." + methodName + "] " +
                "retry error: " + e.getMessage() + "。" +
                "number: " + count);
    }

    /**
     * 日志信息打印
     */
    private void logs(String methodName, String url, long startTime, Object execute) {
        if (logUrl) {
            log.info("HttpClient-util:[" + methodName + "] url:" + url);
        }
        if (logResponse) {
            log.info("HttpClient-util:[" + methodName + "] response:" + execute);
        }
        if (logTime) {
            log.info("HttpClient-util:[" + methodName + "] time:" + (System.currentTimeMillis() - startTime));
        }
    }
}
